#include "pagetable.h"
#include "symbol_table.h"
#include "bpage_complete_test.h"
#include "endian.h"
#include <cstdio>
#include <cassert>

struct StatEntry_t
{
    Iu32 iEntry;                // Entry Value, from 0x24590 to 0x7C1E8, 0xAF8C in all
    int  iCount;                // Count the search hit count
    Iu32 iAddr;                 // log the last hit address
};

static const Iu32 bpe_begin = 0x24590;
static const Iu32 bpe_end = 0x7c1e8;
static const Iu32 bpe_size = (bpe_end - bpe_begin)/8+1;
static StatEntry_t bpe_stat[bpe_size];

static void analyze_pagetable( Iu32 pt_addr, Iu32 size, int action, enum endianness endian );
static void analyze_free_pagetable( Iu32 start, Iu32 end, Iu32 size );

// action = +1 or -1, inc or dec the counter respectively
static void analyze_pagetable( Iu32 pt_addr, Iu32 pagesize, int action, enum endianness endian = LITTLE )
{
    Iu32 *p = (Iu32 *)Dmp_PpcToBuffer(pt_addr);
    //printf( "pt_addr = 0x%08lx, offset = 0x%08lx\n", pt_addr, p );

    Iu32 i, iEntry;
    for(i=0;i<pagesize;i++) {
        iEntry = *(p+i);

        //printf("Val : %x\n",iEntry[i]); 
        if(endian==LITTLE) iEntry = le32toh_(iEntry);
        else if(endian==BIG) iEntry = be32toh_(iEntry);
            
        //printf("Val : %x\n",iEntry); 
        if (iEntry >= bpe_begin && 
            iEntry <= bpe_end && 
            (iEntry % 8) == 0)
        {
            bpe_stat[(iEntry-bpe_begin)>>3].iEntry = iEntry;
            bpe_stat[(iEntry-bpe_begin)>>3].iCount += action;
            bpe_stat[(iEntry-bpe_begin)>>3].iAddr = pt_addr+i*4;
        }
    }
}

static void analyze_free_pagetable(Iu32 start, Iu32 end, Iu32 pagesize)
{
    int i;
    for(i = 0; start + i*4 < end; i++) {
        Iu32 addr = (Iu32)Dmp_PpcToBuffer(start + i*4);
        Iu32 pt = be32toh_(*(Iu32*)addr);
        Iu32 pt_addr = Adr_rTableToPpc(pt);
        //printf("addr = 0x%08lx, pt = 0x%08lx, pt_addr = 0x%08lx\n", addr, pt, pt_addr);
        analyze_pagetable(pt_addr, pagesize, -1);
    }
}

void BPComplete_DoTest()
{
    // Whole pagetable
    analyze_pagetable(PPCADDR_PAGETABLE_AREA, 0x4C0000/4, 1);

    Iu32 stack, start, end;
/* ** Knl_PageTableStack */
/*    Stack Pointer : 0x413b1814->Knl_MemStackC1_t */
/*    Stack Base    : 0x413ab68c->Knl_MemStackC1_t */
/*    Stack Size    : 0x00004680 */
/*    Stack End     : 0x413bd08c */
    stack = Sym_SymbolToAddr("Knl_PageTableStack");
    assert(stack != 0);
    end = be32toh_(*(Iu32*)Dmp_PpcToBuffer(stack));
    start = be32toh_(*(Iu32*)Dmp_PpcToBuffer(stack + 4));
    //printf("%08lx, %08lx", start, end);
    analyze_free_pagetable(start, end, 64);


/* ** Knl_MPageTableStack */
/*    Stack Pointer : 0x41399504->Knl_MemStackC1_t */
/*    Stack Base    : 0x413993ac->Knl_MemStackC1_t */
/*    Stack Size    : 0x00004680 */
/*    Stack End     : 0x413aadac */
    stack = Sym_SymbolToAddr("Knl_MPageTableStack");
    assert(stack != 0);
    end = be32toh_(*(Iu32*)Dmp_PpcToBuffer(stack));
    start = be32toh_(*(Iu32*)Dmp_PpcToBuffer(stack + 4));
    //printf("%08lx, %08lx", start, end);
    analyze_free_pagetable(start, end, 1024);

    // Free Stack
    stack = Sym_SymbolToAddr("LocalADDL0");
    assert(stack != 0);
    start = be32toh_(*(Iu32*)Dmp_PpcToBuffer(stack));
    start = Adr_DramToPpc(start);
    end = Adr_DramToPpc(DRAM_COPYENGINE_AREA + SIZE_COPYENGINE_AREA);
    //printf("%08lx, %08lx\n", start, end);
    analyze_pagetable(start, (end - start)/4, 1, BIG);
    

    /*  Analyze the Data */
    FILE * fpLogFile = fopen("./log.txt", "w");
    for ( Iu32 i = 0; i < bpe_size; i++)
    {   
        if (0 == bpe_stat[i].iCount)
        {
            fprintf(fpLogFile, "Entry: %lx Miss\n", i * 8 + bpe_begin);
            printf( "0x%08lx\n", i * 8 + bpe_begin);
        }
        else
        {
            fprintf(fpLogFile, "Entry: %lx Count: %d \n", i * 8 + bpe_begin, bpe_stat[i].iCount);
        }       
    }
}
